﻿using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;

namespace Medianamik.Core
{
    public class NodePropertyFactory
    {
        public IEnumerable<NodeProperty> CreateNodeProperties(Node node, string cultureName)
        {
            var isLanguageNeutral = cultureName == CultureInfo.InvariantCulture.Name;

            var existingNodeProperties = node.GetNodeProperties(null)
                .Where(p => p.Culture.Name.Equals(cultureName, StringComparison.OrdinalIgnoreCase))
                .ToList();

            var nodeType = node.NodeType;
            
            var regularProps = from p in GetRegularNodeTypeProperties(nodeType)
                               where p.IsLanguageNeutral == isLanguageNeutral
                               join np in existingNodeProperties on p.ID equals np.NodeTypePropertyID 
                               into g
                               from prop in g.DefaultIfEmpty()
                               select prop ?? CreateNodeProperty(node, p, cultureName);

            var lookupProps = from r in nodeType.GetOneToManyRelations()
                              join p in existingNodeProperties on r.ForeignPropertyID equals p.NodeTypePropertyID
                              into g
                              from prop in g.DefaultIfEmpty()
                              where r.ForeignProperty.IsLanguageNeutral == isLanguageNeutral
                              select prop ?? CreateNodeProperty(node, cultureName, r);

            var manyToOneProps = from p in GetManyToOneNodeTypeProperties(nodeType)
                                 where p.IsLanguageNeutral == isLanguageNeutral
                                 join np in existingNodeProperties on p.ID equals np.NodeTypePropertyID
                                 into g
                                 from prop in g.DefaultIfEmpty()
                                 select prop ?? CreateNodeProperty(node, p, cultureName);

            var result = new List<NodeProperty>(regularProps);
            result.AddRange(lookupProps);
            result.AddRange(manyToOneProps);

            if(isLanguageNeutral)
            {
                var manyToManyProps = from r in nodeType.GetManyToManyRelations()
                                      select CreateNodeProperty(node, r);

                result.AddRange(manyToManyProps);
            }

            return result;
        }

        public IEnumerable<NodeTypeProperty> GetRegularNodeTypeProperties(NodeType nodeType)
        {
            return from p in nodeType.GetAllProperties()
                   where p.Relationship == NodeTypePropertyRelationship.Default
                   select p;
        }

        public IEnumerable<NodeTypeProperty> GetManyToOneNodeTypeProperties(NodeType nodeType)
        {
            return from p in nodeType.GetAllProperties()
                   where p.Relationship == NodeTypePropertyRelationship.ManyToOne
                   select p;
        }

        public NodeProperty CreateNodeProperty(Node node, NodeTypeProperty nodeTypeProp, string cultureName)
        {
            return nodeTypeProp.Relationship == NodeTypePropertyRelationship.ManyToOne
                ? new ManyToOneNodeProperty(nodeTypeProp, null, cultureName, node)
                : new NodeProperty(nodeTypeProp, null, cultureName, node);
        }

        public NodeProperty CreateNodeProperty(int? pNodePropertyValueId, string pValue, Node pNode, NodeTypeProperty pNodeTypeProp, string pCultureName, int? pRevision)
        {
            return pNodeTypeProp.Relationship == NodeTypePropertyRelationship.ManyToOne
                ? new ManyToOneNodeProperty(pNodePropertyValueId, pNodeTypeProp, pValue, pCultureName, pRevision, pNode)
                : new NodeProperty(pNodePropertyValueId, pNodeTypeProp, pValue, pCultureName,pRevision, pNode);
        }

        public NodeProperty CreateNodeProperty(Node pNode, string pCultureName, OneToManyRelation pRelation)
        {
            return new LookupNodeProperty(null, pCultureName, pNode, pRelation);
        }

        public NodeProperty CreateNodeProperty(int? pNodePropertyValueId, string pValue, Node pNode, string pCultureName,int? pRevision, OneToManyRelation pRelation)
        {
            return new LookupNodeProperty(pNodePropertyValueId, pValue, pCultureName, pRevision, pNode, pRelation);
        }

        public NodeProperty CreateNodeProperty(Node pNode, ManyToManyRelation pRelation)
        {
            return new ManyToManyNodeProperty(pNode, CultureInfo.InvariantCulture.Name, pRelation); 
        }
    }
}